package org.javaboy.am_demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import java.lang.reflect.Array;
import java.net.http.HttpClient;
import java.util.Arrays;

/**
 * 认证的关键是 AuthenticationManager，这个里边管理了很多 AuthenticationProvider，认证具体是由 AuthenticationProvider 来完成的
 * AuthenticationManager 是可以存在 parent 的，如果自己搞不定，可以交给 parent 去认证。
 * <p>
 * 默认情况下，AuthenticationManager 就是存在 parent 的。
 * 默认情况下，Spring Security 向 Spring 容器注册了一个 AuthenticationManager，这个就是全局的 AuthenticationManager，也就是所谓的 parent
 * 如果是想要配置局部的 AuthenticationManager，那么可以直接设置给过滤器链.
 * <p>
 * 全局的 AuthenticationManager 是系统自动配置的，那么全局的 UserDetailsService 怎么配置呢？当然就是直接向 Spring 容器注册一个 UserDetailsService 实例即可
 */
@Configuration
public class SecurityConfig {

    @Bean
    UserDetailsService globalUs() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("{noop}123").build());
        return manager;
    }

    UserDetailsService appUs() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("lisi").password("{noop}123").build());
        return manager;
    }

    UserDetailsService webUs() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("wangwu").password("{noop}123").build());
        return manager;
    }

    /**
     * 这个过滤器专门处理来自手机的请求，拦截规则是 /app/**
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    @Order(1)
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.securityMatcher("/app/**")
                .authorizeHttpRequests(a -> a.anyRequest().authenticated())
                //这里配置的 AuthenticationManager 就是一个局部的 AuthenticationManager，也就是仅仅在当前过滤器链中使用的 AuthenticationManager，正常来说，当一个请求到达的时候，都是由这个 AuthenticationManager 去处理的（如果自己没有配置这个 AuthenticationManager，系统会自动配置），如果这个 AuthenticationManager 处理不了当前的认证，那么就会交给 parent 去处理，Parent 就是所谓的全局的 AuthenticationManager了
                //这里可以不用配置，因为 HttpSecurity 默认情况下就有这个东西
//                .authenticationManager(appAuthenticationManager())
                //由于当前过滤器链中就存在 AuthenticationManager 实例，而且这个实例需要用到 userDetailsService，这里就配置这个 userDetailsService
                .userDetailsService(appUs())
                .formLogin(f -> f.loginProcessingUrl("/app/login").successHandler((req,resp,auth)->{
                    resp.getWriter().write(auth.getName());
                }).permitAll())
                .csrf(c -> c.disable());
        return http.build();
    }

    /**
     * 专门处理来自 Web 的请求，拦截规则就是 /web/**
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    @Order(2)
    SecurityFilterChain securityFilterChain2(HttpSecurity http) throws Exception {
        http.securityMatcher("/web/**").authorizeHttpRequests(a -> a.anyRequest().authenticated())
                //这里配置的 AuthenticationManager 就是一个局部的 AuthenticationManager，也就是仅仅在当前过滤器链中使用的 AuthenticationManager，正常来说，当一个请求到达的时候，都是由这个 AuthenticationManager 去处理的（如果自己没有配置这个 AuthenticationManager，系统会自动配置），如果这个 AuthenticationManager 处理不了当前的认证，那么就会交给 parent 去处理，Parent 就是所谓的全局的 AuthenticationManager了
//                .authenticationManager(webAuthenticationManager())
                .userDetailsService(webUs())
                .formLogin(f -> f.loginProcessingUrl("/web/login").successHandler((req,resp,auth)->{
                    resp.getWriter().write(auth.getName());
                }).permitAll())
                .csrf(c -> c.disable());
        return http.build();
    }
}
